#ifndef MENU_MANAGER_H
#define MENU_MANAGER_H

#include "widgets/menu.h"
#include "main_widget.h"

class MenuManager;

/* -------------------------- MenuManagerListener ---------------------------------------------- */
class MenuManagerListener
{
public:
    virtual void OnMenuOpened(MenuManager* mm, Menu* m) = 0;
    virtual void OnMenuClosed(MenuManager* mm, Menu* m) = 0;
    virtual void OnMenuClick(MenuManager* mm, Menu* m, QMouseEvent* e) = 0;
};
/* --------------------------------------------------------------------------------------------- */

/* ============================ MenuManager ==================================================== */
class MenuManager: public Observable< MenuManagerListener >, public MenuListener, public PulseObserver,
                   public AnimationObserver
{
public:
    /* -------------------------------- typedefs ----------------------------------------------- */
    typedef vector< MenuManagerListener* > Listeners;
    typedef map< int, vector< Menu* > > SubMenus;
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------ constructor and destructor ------------------------------------- */
    MenuManager(Engine* _engine, MainWidget* _mw):engine(_engine), mw(_mw), submenuAnime(_engine->fps, 400)
    {
        visible = false;
        mainToOpen = -1;
        p = new Pulse(4, submenuAnime.GetTime(), 0, 0);
        p->AddObserver(this);

        // menu creation
        int id = 0;

        QString main[6] = {"Song", "Playlist", "Stats", "Options", "Help", "Exit"};
        for(int i=0; i<6; i++)
        {
            CreateMenu(id++, 0, 25 + 30 * i, main[i], i==5? true: false);
        }

        id = 100;

        QString song[6] = {"Open", "Save", "Save As..", "Player", "Edit", "Recent"};
        for(int i=0; i<6; i++)
        {
            CreateMenu(id++, 20, 55 + 30 * i, song[i], true, 0);
        }

        id = 200;

        QString playlist[7] = {"New", "Open", "Save", "Save As..", "Edit", "Close", "Recent"};
        for(int i=0; i<7; i++)
        {
            CreateMenu(id++, 20, 85 + 30 * i, playlist[i], true, 1);
        }

        id = 300;

        QString stats[3] = {"Session Stats", "Song Stats", "Playlist Stats"};
        for(int i=0; i<3; i++)
        {
            CreateMenu(id++, 20, 115 + 30 * i, stats[i], true, 2);
        }

        id = 400;

        QString options[4] = {"Option 1", "Option 2", "Option 3", "Option 4"};
        for(int i=0; i<4; i++)
        {
            CreateMenu(id++, 20, 145 + 30 * i, options[i], true, 3);
        }

        id = 500;

        QString help[2] = {"Contents", "About"};
        for(int i=0; i<2; i++)
        {
            CreateMenu(id++, 20, 175 + 30 * i, help[i], true, 4);
        }

        locked = false;
        opening = false;
        submenuAnime.AddObserver(this);
        engine->AddAnimation(&submenuAnime);
    }

    virtual ~MenuManager(){}
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------------ visibility functions ------------------------------------- */
    void SetVisible(bool set)
    {
        if(visible == set) return;
        visible = set;
        for(int i=0; i<6; i++)
        {
            menus[i]->SetVisible(set, 300);
        }
    }

    void OpenMenu(int menuId)
    {
        locked = true;
        int submenusCount = submenus[menuId].size();
        int yOffset = 30 * submenusCount + 20;
        submenuAnime.SetDuration(80 * submenusCount);
        p->Disable();
        submenuAnime.Stop();
        p->Enable();
        p->SetPulseCount(submenusCount-1);
        mainToOpen = menuId;
        opening = true;

        IntGraph::IType* interpolator;
        for(int i=0; i<6; i++)
        {
            interpolator = g.Get(main_id[i]);
            interpolator->SetEnabled(i > menuId);
            interpolator->SetMaxY(menus[i]->GetY() + yOffset);
        }
        current = menus[menuId];
        submenuAnime.Play();
    }

    void CloseMenu()
    {
        p->Disable();
        p->Reset();
        p->Enable();
        submenuAnime.Reverse(true);
        opening = false;
        locked = true;
        submenuAnime.Play();
    }
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------- PulseListener implementation ---------------------------------- */
    void OnPulse(int pulseIndex)
    {
        if(opening)
        {
            submenus[mainToOpen][pulseIndex]->SetVisible(true, 300);
        } else
        {
            submenus[mainToOpen][pulseIndex]->SetVisible(false, -1);
        }
    }
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------ MenuListener implementation ------------------------------------ */
    void OnClick(Menu* m, QMouseEvent* event)
    {
        if(locked) return;
        for(Listeners::iterator i=observers->begin(); i!=observers->end(); ++i)
        {
            (*i)->OnMenuClick(this, m, event);
        }
    }
    /* ----------------------------------------------------------------------------------------- */

    /* ---------------------- AnimationListener implementation --------------------------------- */
    virtual void OnPause(Animation* a){};
    virtual void OnStop(Animation* a){};
    virtual void OnEnd(Animation* a)
    {
        locked = false;
        if(!opening)
        {
            menus[mainToOpen]->SetSelected(false);
            for(Listeners::iterator i=observers->begin(); i!=observers->end(); ++i)
            {
                (*i)->OnMenuClosed(this, menus[mainToOpen]);
            }
        }else
        {
            for(Listeners::iterator i=observers->begin(); i!=observers->end(); ++i)
            {
                (*i)->OnMenuOpened(this, menus[mainToOpen]);
            }
        }
    };
    virtual void OnPlay(Animation* a){};
    /* ----------------------------------------------------------------------------------------- */

    /* ---------------- Menu id constants (initialized in initializer.cpp) --------------------- */
    static int SONG;                // main menus
    static int PLAYLIST;
    static int STATS;
    static int OPTIONS;
    static int HELP;
    static int EXIT;

    static int OPEN_SONG;           // Song submenu
    static int SAVE_SONG;
    static int SAVE_AS_SONG;
    static int PLAYER;
    static int EDIT_SONG;
    static int RECENT_SONG;

    static int NEW_PLAYLIST;        // Playlist submenu
    static int OPEN_PLAYLIST;
    static int SAVE_PLAYLIST;
    static int SAVE_AS_PLAYLIST;
    static int EDIT_PLAYLIST;
    static int CLOSE_PLAYLIST;
    static int RECENT_PLAYLIST;

    static int OPTION_1;            // Options submenu
    static int OPTION_2;
    static int OPTION_3;
    static int OPTION_4;

    static int CONTENTS;            // Help submenu
    static int ABOUT;
    /* ----------------------------------------------------------------------------------------- */

    /* -------------------------------- public fields ------------------------------------------ */
    vector< Menu* > menus;
    SubMenus submenus;
    /* ----------------------------------------------------------------------------------------- */

private:
    /* ---------------------------------- private fields --------------------------------------- */
    Engine* engine;
    MainWidget* mw;
    bool visible;
    bool opening;
    bool locked;
    Animation submenuAnime;
    Pulse* p;
    int mainToOpen;
    int main_id[6];
    IntGraph g;
    Menu* current;
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------------- private functions --------------------------------------- */
    void CreateMenu(int id, int _x, int _y, QString& text, bool submenu, int mainMenuIndex = -1)
    {
        Menu* m = new Menu(engine, id, _x, _y, text, submenu);
        m->AddObserver(this);
        menus.push_back(m);
        if(submenu && id>5) submenus[mainMenuIndex].push_back(m);
        if(!submenu || text == "Exit")
        {
            main_id[id] = g.AddInterpolator(submenuAnime.GetTime(), 0, 0, Interpolable::GetProperty<int>(m, "y"), _y, _y);
        }
        mw->AddGui(m);
    }
    /* ----------------------------------------------------------------------------------------- */
};
/* ============================================================================================= */

#endif // MENU_MANAGER_H
